package project;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.image.renderable.ParameterBlock;
import java.math.BigDecimal;
import java.io.*;
import java.util.*;

import javax.media.jai.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.*;
import javax.media.jai.operator.TransposeDescriptor;
import javax.swing.JOptionPane;
import javax.swing.border.*;
import static project.GUIComponents.*;
import static project.PixieFactory.*;
import static project.PixieClasses.*;
import static project.PixieConstants.*;

@SuppressWarnings("serial")
public class Pixila extends JFrame
{
	public static Pixila dixie;

	public static float getAspectRatio()
	{
		return ((float)currentImage.getWidth()/(float)currentImage.getHeight());
	}

	public static RenderedImage resizeImage(RenderedImage img, float scaleX, float scaleY)
	{
		Interpolation interpParam = Interpolation.getInstance(Interpolation.INTERP_BICUBIC); 
		Vector<Object> src = new Vector<Object>(Arrays.asList(new RenderedImage[] {img}));
		Vector<Object> params = new Vector<Object>(Arrays.asList(new Object[] {scaleX, scaleY, 0.0F, 0.0F, interpParam}));
		ParameterBlock resizeParameters = new ParameterBlock(src, params);
		return JAI.create("scale", resizeParameters);
	}

	public static float getFitScale(){
		float fitScale = 1.0F;
		int spaceWidth = displayArea.getWidth()-10;
		int spaceHeight = displayArea.getHeight()-10;
		float xScale = (float)spaceWidth/(float)currentImage.getWidth();
		float yScale = (float)spaceHeight/(float)currentImage.getHeight();

		/**
		 * If the width of the image is longer than the width of the available space
		 * OR, the height of image is longer than the height of the available space,
		 * select a fit-to-screen ratio which is the smaller of the two ratios.
		 */
		if (currentImage.getWidth()>=spaceWidth || currentImage.getHeight()>=spaceHeight)
			fitScale = (xScale <= yScale) ? xScale : yScale;

		return fitScale;
	}

	public static void fitToScreen(RenderedImage img){
		float scale = getFitScale();
		imageHolder.set(resizeImage(img, scale, scale));
		BigDecimal fitTo2dp = new BigDecimal(scale);
		fitTo2dp = fitTo2dp.setScale(2, BigDecimal.ROUND_HALF_UP);
		scale = fitTo2dp.floatValue();
		int position = (int)(scale*100 - 100);
		zoomSlider.setValue(position);
	}

	private void loadImage(){
		try{
			currentImage = JAI.create("fileload", currentFilePath);
		}catch(Exception e){
			ExceptionViewer.show("Wrong Image Type", "The file you selected " +
					"'"+currentFilePath+"' may not exist or is not an image file.", e);
		}
		updateDisplay();
		displaySwitch.setFull();
		clearToDoStacks();
		topMenu.setSelectedIndex(1);
	}

	private RenderedImage transposeImage(RenderedImage img, final String transposeType){
		addToUndoStack();
		if(transposeType.equals("flip_v"))
			return( JAI.create("transpose", img, TransposeDescriptor.FLIP_VERTICAL));
		else if (transposeType.equals("flip_h"))
			return( JAI.create("transpose", img, TransposeDescriptor.FLIP_HORIZONTAL));
		else if (transposeType.equals("clockwise"))
			return( JAI.create("transpose", img, TransposeDescriptor.ROTATE_90));
		else if (transposeType.equals("counterclockwise"))
			return( JAI.create("transpose", img, TransposeDescriptor.ROTATE_270));
		else
			return(img);
	}

	public static void saveAsFormat(RenderedImage img, String fileFormat){
		String fileExtension = fileFormat.equals("JPEG") ? "jpg" : fileFormat.toLowerCase();

		fileDialog.setDialogTitle ("Save Image As..");
		FileFilter formatFilter = new FileNameExtensionFilter(fileFormat,fileExtension);
		fileDialog.addChoosableFileFilter(formatFilter);

		//set default file name for saving
		File savingFile = new File(currentFile.getParent(), "Untitled");
		fileDialog.setSelectedFile(savingFile);

		//on approval, get file path and save
		int check = fileDialog.showSaveDialog(null);
		if(check == JFileChooser.APPROVE_OPTION){
			savingFile = fileDialog.getSelectedFile();
			String savePath = savingFile.getAbsolutePath() + "."+fileExtension;
			try{
				save(img, savePath, fileFormat);
				currentFilePath = savePath;
				currentFile = new File(currentFilePath);
				//this.setTitle(currentFile.getName() + " - PIXILA");
			}catch(IllegalArgumentException e){
				JOptionPane.showMessageDialog(dixie, "Enter a valid file name.",
						"Saving Error!", JOptionPane.ERROR_MESSAGE);
			}
		}
		fileDialog.removeChoosableFileFilter(formatFilter);
	}

	public static void save(RenderedImage img, String path, String format){
		try{
			JAI.create("filestore", img, path, format, null);
			bottomPanelMessage.setText("Saved changes to: " + path);
			changesSaved = true;
			saveButton.setIcon(retrieveIcon(saveIconPath));
		}catch(IllegalArgumentException e){
			JOptionPane.showMessageDialog(dixie, "Cannot save as extension: " + format,
					"Saving Error", JOptionPane.WARNING_MESSAGE);			
		} 
	}

	public void saveChanges(){ 
		int len = currentFilePath.length();
		//get index of '.' get the characters after it as the extension
		int dotIndex = currentFilePath.lastIndexOf('.');

		String ext = currentFilePath.substring(dotIndex+1, len);
		String format = ext.toUpperCase();
		if(ext.equals("jpg")) //formats are d uppercase of extensions except for JPEG
			format = "JPEG";

		save(currentImage, currentFilePath, format);
	}

	private RenderedImage sharpenImage(RenderedImage img){
		return JAI.create("convolve", img, PixieConstants.SHARPEN);
	}

	private RenderedImage blurImage(RenderedImage img){
		return JAI.create("convolve", img, PixieConstants.BLUR);
	} 

	private RenderedImage adjustBrightness(RenderedImage img, double[] multiplier){
		return JAI.create("rescale", img, multiplier);
	}

	private void undoLastAction(){
		if(undoStack != null && !undoStack.empty()) { //[REF 1]
			redoStack.push(currentImage);
			redoButton.setEnabled(true);
			currentImage = undoStack.pop(); //get last image added to the stack
			changesSaved = false;
			updateDisplay();
			if(undoStack.isEmpty()){
				changesSaved = true;//if all changes are undone, no need to save
				undoButton.setEnabled(false);
				saveButton.setIcon(retrieveIcon(saveIconPath));
			}
		}
	}

	private void redoLastAction(){
		if(redoStack != null && !redoStack.empty()){
			undoStack.push(currentImage);
			currentImage = redoStack.pop(); //get last image before undo
			updateDisplay();
			undoButton.setEnabled(true);
			saveButton.setIcon(retrieveIcon(notSavedIconPath));
			if(redoStack.isEmpty())
				redoButton.setEnabled(false);
			changesSaved = false;
		}
	}

	public static void addToUndoStack(){//b4 every new action, add current image to undo stack
		/**
		 * Maintain fixed size of undo stack.
		 */
		if(undoStack.size() >= PixieConstants.UNDO_MAX)
			undoStack.pop();
		
		/**Add current image to the stack.*/
		undoStack.push(currentImage);
		redoStack.clear();//reset redo stack
		changesSaved = false;
		undoButton.setEnabled(true);
		redoButton.setEnabled(false);//disable redo button
		saveButton.setIcon(retrieveIcon(notSavedIconPath));
	}

	private void clearToDoStacks(){//reset redo and undo stack
		redoStack.clear();
		undoStack.clear();
		changesSaved = true;
		undoButton.setEnabled(false);
		redoButton.setEnabled(false);
		saveButton.setIcon(retrieveIcon(saveIconPath));
	}

	private RenderedImage invertColors(RenderedImage img){
		return JAI.create("Invert", img);//invert color
	}

	private RenderedImage grayConvert(RenderedImage img){
		ParameterBlock toneParameters = new ParameterBlock();
		toneParameters.addSource(img);
		toneParameters.add(PixieConstants.GRAY_TONE);
		try{
			return JAI.create("bandcombine", toneParameters); //apply filter
		}catch(IllegalArgumentException e){
			JOptionPane.showMessageDialog(dixie, "Error converting image to grayscale.", "Color Conversion Error", JOptionPane.ERROR_MESSAGE);
			return img;//if error, return initial image
		}
	}

	private RenderedImage getEdge(RenderedImage src){
		//Perform edge detection operation.
		RenderedImage dst = JAI.create("gradientmagnitude", src, PixieConstants.X_EdgeDetector, PixieConstants.Y_EdgeDetector);
		
		/**
		 * Edge detection gives negative image. 
		 * Invert the Colors. Convert to grayscale.
		 */
		return grayConvert(invertColors(dst));
	}

	private RenderedImage pencilBody(RenderedImage img){
		//img = sharpenImage(blurImage(sharpenImage(img)));
		img = grayConvert(img);
		int n =1;
		while(n<=10){
			img = JAI.create("rescale", img, PixieConstants.BRIGHTEN, PixieConstants.BRIGHT_SUB);
			n++;
		}
		return img;
	}

	private RenderedImage sketchImage(RenderedImage img){
		addToUndoStack();
		float fit = getFitScale();
		float scaleUp = 1.0F/fit;
		img = resizeImage(img, fit, fit);

		RenderedImage outline = getEdge(img);
		outline = adjustBrightness(outline, PixieConstants.BRIGHTEN);
		
		RenderedImage sketch = sharpenImage(outline);
		imageHolder.set(sketch);

		if(addShade()){
			RenderedImage body = pencilBody (img);
			sketch  = JAI.create("and", body, outline);
		}

		sketch = resizeImage(sketch, scaleUp, scaleUp);
		return sketch;

	}

	private boolean addShade(){
		Object[] options = {"Shade", "Don't Shade"};
		int choice = JOptionPane.showOptionDialog(this,
				"Would you like to shade this drawing?", "Shade This Drawing",
				JOptionPane.YES_NO_OPTION, 
				JOptionPane.PLAIN_MESSAGE, null, options, options[1]);
		if(choice==JOptionPane.YES_OPTION) 
			return true;
		else
			return false;
	}

	private RenderedImage getBody(RenderedImage img){
		for(int i =1; i<=7;i++){
			img = blurImage(img);
			if(i%2 == 0)
				img = adjustBrightness(img, PixieConstants.BRIGHTEN);
		}
		return img;
	}

	private RenderedImage paintImage(RenderedImage img){
		addToUndoStack();
		float scaleDown = 1.0F;
		if (overSizedImage(img)){//scale down to avoid crashing
			scaleDown = 1000.0F/(float)img.getWidth();
			img = resizeImage(img, scaleDown, scaleDown);
		}

		img = blurImage(img);
		RenderedImage outline = getEdge(img);
		for(int i =1; i<=1;i++){
			outline = adjustBrightness(outline, PixieConstants.BRIGHTEN);
		}
		RenderedImage body = getBody(img);
		RenderedImage painted = JAI.create("and", outline, body);

		//if it was scaled down due to size, scale it back to normal size
		float scaleUp = 1.0f/scaleDown;
		painted = resizeImage(painted, scaleUp, scaleUp);
		return painted;
	}

	private boolean overSizedImage(RenderedImage img){
		if(img.getWidth()>1000 || img.getHeight()>1000)
			return true;
		else
			return false;
	}

	public static void updateDisplay(){
		if(currentImage != null){
			fitToScreen(currentImage);
			updateSizeInfo();
		}
	}

	public static void updateSizeInfo(){
		sizeLabel.setText("Image Size: "+ currentImage.getWidth()+
				" x "+currentImage.getHeight());
		
		newWidthInput.setValue(currentImage.getWidth());
		newHeightInput.setValue(currentImage.getHeight());
		scaleInput.setValue(100);
	}

	public static void showFullSize(){
		imageHolder.set(currentImage);
		zoomLevel.setText("Display Size: 100%");
		updateSizeInfo();
		zoomSlider.setValue(0);	//reset slider to the origin 
	}

	public void openImageFromFile() {
		if(changesSaved || (!changesSaved && askToSaveChanges())){
			fileDialog.setDialogTitle ("Open Image");
			FileFilter imageFilter = new FileNameExtensionFilter(
					"Image files (JPEG, PNG, BMP, TIFF, GIF)", "jpg", "jpeg", "png", "bmp", "tiff", "gif");
			fileDialog.addChoosableFileFilter(imageFilter);
			File newImageFile = new File(currentFile.getPath());//get last file path
			fileDialog.setSelectedFile(currentFile);

			int dialogOption = fileDialog.showOpenDialog(this);
			if (dialogOption == JFileChooser.APPROVE_OPTION){
				currentFile = fileDialog.getSelectedFile();
				currentFilePath = currentFile.getAbsolutePath().replace('\\', '/');
				try{
					loadImage();
					this.setTitle(currentFile.getName() + " - PIXILA");
				}catch(RuntimeException e){
					JOptionPane.showMessageDialog(this, "Select an Image file!",
							"Wrong File Type", JOptionPane.WARNING_MESSAGE);
					currentFilePath = newImageFile.getAbsolutePath();
					currentFile = newImageFile;
					loadImage();
				}
			}
			fileDialog.removeChoosableFileFilter(imageFilter);
		}
	}


	public void sliderStateMonitor() {
		int x = zoomSlider.getValue();
		float currentScale = 1.0f;
		if (x <= -100)
			currentScale = 0.01f;
		else if(x > -100 && x <= 100)
			currentScale = (float)(100+x)/100f;
		else
			currentScale = 1.0f;

		RenderedImage currentDisplay = resizeImage(currentImage, currentScale, currentScale);
		imageHolder.set(currentDisplay);
		zoomLevel.setText("Display Size: "+ (int)(currentScale*100f) + "%");
	}

	public static void widthMonitor(){
		try{
			int newX = newWidthInput.getNumValue();
			int newY = (int)((float)newX/getAspectRatio());
			newHeightInput.setValue(newY);
			
			//update scale
			float scale = (float)newX/(float)currentImage.getWidth();
			scaleInput.setValue((int)(scale*100));
		}catch(NumberFormatException e){}
	}

	public static void heightMonitor(){
		try{
			int newY = newHeightInput.getNumValue();
			int newX = (int)((float)newY * getAspectRatio());
			newWidthInput.setValue(newX);
			
			//update scale
			float scale = (float)newY/(float)currentImage.getHeight();
			scaleInput.setValue((int)(scale*100));
		}catch(NumberFormatException e){}
	}

	public static void scaleMonitor(){
		try{
			float scale = ((Integer)scaleInput.getValue()).floatValue()/100F;

			int newX = (int) (currentImage.getWidth() * scale);
			newWidthInput.setValue(newX);

			int newY = (int) (currentImage.getHeight() * scale);
			newHeightInput.setValue(newY);
		}catch(NumberFormatException e){}
	}

	private void customResize() {
		addToUndoStack(); //update undo list
		try{
			int customWidth = newWidthInput.getNumValue();
			int customHeight = newHeightInput.getNumValue();
			currentImage = resizeBySize(currentImage, customWidth, customHeight);
			showFullSize();
		}catch(NumberFormatException e){
			JOptionPane.showMessageDialog(this, "Enter only numbers.",
					"Resize Error", JOptionPane.WARNING_MESSAGE);
		}
	}

	private RenderedImage resizeBySize(RenderedImage img, int newWidth, int newHeight){
		float xScale = (float)newWidth/(float)img.getWidth();
		float yScale = (float)newHeight/(float)img.getHeight();
		return resizeImage(img, xScale, yScale);
	} 

	private boolean askToSaveChanges(){
		Object[] options = {"Save", "Discard", "Cancel"};
		int choice = JOptionPane.showOptionDialog(this, "Do you want to save these changes?",
				"You made changes!", JOptionPane.YES_NO_CANCEL_OPTION,
				JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
		if(choice==JOptionPane.YES_OPTION){
			saveChanges();
			return true;
		}
		else if(choice==JOptionPane.NO_OPTION)
			return true;
		else
			return false;
	}

	private void exitProcedure(){
		if(changesSaved)
			System.exit(0);
		else if(askToSaveChanges())
			System.exit(0);
		else
			updateDisplay();
	}

	private void initializeComponents() {
		//Window properties
		this.setExtendedState(this.getExtendedState() | JFrame.MAXIMIZED_BOTH);
		this.setIconImage(retrieveIcon(programLogoPath).getImage());
		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		this.addWindowListener(new WindowAdapter(){//[REF 2]
			public void windowClosing(WindowEvent e){
				exitProcedure();
			}
		});
		this.addWindowStateListener(new WindowAdapter(){
			public void windowStateChanged(WindowEvent e){
				updateDisplay();
				
				System.out.println("orig:"+editBarScoller.getSize());
				
				editToolbar.setSize(editToolbar.getPreferredSize());
				editToolbar.validate();
				editBarScoller.setSize(editBarScoller.getPreferredSize());
				editBarScoller.validate();
				
				System.out.println("after:"+editBarScoller.getSize());
			}
		});
		//this.setMinimumSize(new Dimension(760, 700));
		//TOP Panel
		topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.PAGE_AXIS));
		topPanel.add(topMenu);
		topMenu.setFocusable(false);
		topMenu.setFont(new Font("Garamond", 1, 13));
		topMenu.setBorder(null);
		topMenu.addTab("FILE", fileMenuPanel);
		topMenu.addTab("EDIT", editBarScoller);
		topMenu.addTab("About", new JPanel());

		addComponents(fileMenuPanel, new FlowLayout(FlowLayout.LEADING,10,1), openFileButton, saveButton, saveAsButton, exitButton);

		openFileButton.addActionListener(ActionFactory.openFileAction);

		saveButton.addActionListener(ActionFactory.saveAction);

		saveAsButton.addActionListener(ActionFactory.saveAsAction);

		addComponents(saveAsMenu, new BoxLayout(saveAsMenu, BoxLayout.Y_AXIS) ,saveJPEG,savePNG,saveBMP,saveTIFF);

		exitButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				exitProcedure();
			}
		});
		//EDITMenuPanel
		//EditToolbar
		addComponents(editToolbar, new FlowLayout(FlowLayout.LEADING,5,1), undoButton, redoButton, separator(40), flipVertical,
				flipHorizontal, separator(40), rotateCounterClockwise, rotateClockwise, separator(40), 
				increaseBrightness, decreaseBrightness, separator(40), sharpenImageButton, blurImageButton, 
				separator(40),resizeButton, new JLabel(" "), artButton, new JLabel(" "), colorToneButton);

		undoButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				undoLastAction();
			}
		});

		redoButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				redoLastAction();
			}
		});

		flipVertical.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				currentImage = transposeImage(currentImage, "flip_v");
				updateDisplay();
			}
		});

		flipHorizontal.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				currentImage = transposeImage(currentImage, "flip_h");
				updateDisplay();
			}
		});

		rotateCounterClockwise.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				currentImage = transposeImage(currentImage, "counterclockwise");
				updateDisplay();
			}
		});

		rotateClockwise.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				currentImage = transposeImage(currentImage, "clockwise");
				updateDisplay();
			}
		});

		increaseBrightness.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				addToUndoStack();
				currentImage = adjustBrightness(currentImage, PixieConstants.BRIGHTEN);
				updateDisplay();
			}
		});

		decreaseBrightness.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				addToUndoStack();
				currentImage = adjustBrightness(currentImage, PixieConstants.DIM);
				updateDisplay();
			}
		});

		sharpenImageButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				addToUndoStack();
				currentImage = sharpenImage(currentImage);
				updateDisplay();
			}
		});

		blurImageButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				addToUndoStack();
				currentImage = blurImage(currentImage);
				updateDisplay();
			}
		});

		sketchButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				try{
					currentImage = sketchImage(currentImage);
					updateDisplay();
				}catch(IllegalArgumentException e){}
			}
		});

		colorPaintButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				try{
					currentImage = paintImage(currentImage);
					updateDisplay();
				}catch(IllegalArgumentException e){}
			}
		});

		//artButton properties
		addComponents(artMenu, new FlowLayout(FlowLayout.CENTER, 2, 4) ,sketchButton, colorPaintButton);
		artMenu.setPreferredSize(new Dimension(120, 80));
		artButton.setPopUpMenu(artMenu);
		
		resizeButton.setPopUpMenu(resizeMenu);

		scaleInput.setPreferredSize(newHeightInput.getPreferredSize());
		//newSizeButton.setPreferredSize(new Dimension(100,30));
		JLabel wLabel = new JLabel("New Width: ");
		JLabel hLabel = new JLabel("New Height: ");
		JLabel slabel = new JLabel("Scale (%): ");
		setFonts(new Font("Garamond", Font.PLAIN, 15), wLabel, hLabel, slabel, scaleInput, newSizeButton);
		addComponents(ratioPanel, new FlowLayout(FlowLayout.LEADING,5,5), ratioCheck);

		resizePanel.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
		addComponents(resizePanel, new GridLayout(3, 4, 5, 5), wLabel, newWidthInput, hLabel, newHeightInput,
				slabel, scaleInput, new JLabel(), new JLabel(), new JLabel(), newSizeButton);
		
		addComponents(resizeMenu, new BoxLayout(resizeMenu, BoxLayout.Y_AXIS), ratioPanel, new JSeparator(JSeparator.HORIZONTAL), resizePanel);
		ratioCheck.addActionListener(ActionFactory.ratioListener);

		newSizeButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				customResize();
			}
		});

		//end resize panel

		//colorMenu properties
		colorMenu.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
		addComponents(colorMenu, new GridLayout(5, 3, 5, 5), new toneItem("Grayscale", Color.LIGHT_GRAY, PixieConstants.GRAY_TONE),
				new toneItem("Sepia Tone", new Color(188, 76, 36), PixieConstants.SEPIA_TONE),
				new toneItem("Cool Blue", new Color(150,150,255), PixieConstants.LIGHTBLUE_TONE), 
				new toneItem("Deep Blue", Color.BLUE, PixieConstants.BLUE_TONE), 
				new toneItem("Red Blaze", Color.RED, PixieConstants.RED_TONE), 
				new toneItem("Forest Green", Color.GREEN, PixieConstants.GREEN_TONE), 
				new toneItem("Bright Red", new Color(255,150,150), PixieConstants.LIGHTRED_TONE), 
				new toneItem("Luminous Green", new Color(120,255,120), PixieConstants.LIGHTGREEN_TONE), 
				new toneItem("Purple Haze", Color.MAGENTA, PixieConstants.PURPLE_TONE), 
				new toneItem("Pink Picnic", Color.PINK, PixieConstants.PINK_TONE), 
				new toneItem("Cyan Tint", Color.CYAN, PixieConstants.CYAN_TONE), 
				new toneItem("Turquoise Madness", new Color(120,255,255), PixieConstants.TURQUOISE_TONE), 
				new toneItem("Golden Sun", new Color(200,200,0), PixieConstants.GOLDEN_TONE), 
				new toneItem("Bright Yellow", Color.YELLOW, PixieConstants.YELLOW_TONE), 
				new toneItem("Negative", Color.BLACK, PixieConstants.NEGATIVE_TONE)
				);
		colorToneButton.setPopUpMenu(colorMenu);

//end EDITMenuPanel
//end TOP Panel
//DISPLAY AREA
		displayArea.setViewportView(displayAreaPanel);
		displayArea.setWheelScrollingEnabled(true);
		displayArea.setBorder(null);
		displayArea.setPreferredSize(new Dimension(700, 500));
		displayAreaPanel.setBackground(Color.black);
		displayAreaPanel.setLayout(new GridBagLayout());
		displayAreaPanel.add(imageHolder);
		displayAreaPanel.setBorder(null);
//end Display Area
//BOTTOM Panel
		//Initialize labels
		Font baseFont = new Font("Garamond", Font.PLAIN, 14);
		setFonts(baseFont, bottomPanelMessage, mousePositionTracker, sizeLabel, zoomLevel, ratioCheck);
		setForegrounds(Color.WHITE, mousePositionTracker, sizeLabel, zoomLevel);
		
		//Zoom Slider properties
		zoomSlider.setPreferredSize(new Dimension(200, 20));
		zoomSlider.setMaximum(100);
		zoomSlider.setMinimum(-100);
		zoomSlider.addChangeListener(ActionFactory.sliderListener);
		zoomSlider.setFocusable(false);
		
		addComponents(imageSizePanel, new FlowLayout(FlowLayout.LEFT), sizeLabel);
		addComponents(zoomPanel, new FlowLayout(FlowLayout.CENTER), zoomLevel);
		addComponents(mouseMotionPanel, new FlowLayout(FlowLayout.RIGHT), mousePositionTracker);
		addComponents(msgPanel, new GridLayout(1, 3, 10, 40), imageSizePanel,zoomPanel ,mouseMotionPanel );
		setBackgrounds(new Color(70, 70, 70), msgPanel, zoomPanel,mouseMotionPanel, imageSizePanel);
		//end BOTTOM Panel
		
		//LAYOUT ARRANGEMENTS for Bottom Panel
		addComponents(bottomPanel, new FlowLayout(FlowLayout.CENTER, 5, 10), displaySwitch, zoomSlider);  

		//layout for Message Bar
		messageBar.setLayout(new BorderLayout());
		messageBar.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
		messageBar.add(bottomPanelMessage, BorderLayout.LINE_START);
		
	//end LAYOUT ARRANGEMENTS
		//MidLayer Layout
		addComponents(midLayer, new BoxLayout(midLayer, BoxLayout.Y_AXIS), displayArea, msgPanel);  
		
//end Tools Panel
		
		//Arrangement on Window
		JPanel mainPanel = new JPanel();
		this.setContentPane(mainPanel);
		addComponents(mainPanel, new BoxLayout(mainPanel, BoxLayout.Y_AXIS), topPanel, midLayer, bottomPanel, messageBar);
		this.pack();
}//end initializeComponents

	public Pixila() {
		initializeComponents();
		currentFile = new File(defaultFilePath);
		currentFilePath = currentFile.getAbsolutePath();
		setTitle("Pixie Dixie");
		topMenu.setSelectedIndex(1);
	}

	public static void main(String args[]){
		try {
			System.setProperty("com.sun.media.jai.disableMediaLib", "true");
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		}catch (Exception e) {}

/*		Thread progThread;
		Runnable progRun = new Runnable() {
			public void run() {
				boolean started = true;
				while(started){
*/					try{
						dixie = new Pixila();
						dixie.setExtendedState(JFrame.MAXIMIZED_BOTH);
						currentImage=null;
						dixie.loadImage();
						dixie.setVisible(true);
						//throw new NullPointerException();
					}catch(IllegalArgumentException e){JOptionPane.showMessageDialog(dixie, "Illegal Argument.");
					}catch(NullPointerException e){
						ExceptionViewer.show("Exception in Program", null, e);
						//started = false;
					}
					
/*				}
			}
		};
		
		progThread = new Thread(progRun);

		SwingUtilities.invokeLater(progThread);
*/		
		//System.exit(0);
	}
	
	public static void showTrace(Exception exc){
/*		String str = "Something is Null.\n\n";
		StringBuilder sb = new StringBuilder();
		for(StackTraceElement elem : exc.getStackTrace()) 
			sb.append(elem.toString()+"\n");
*/		
	}
}//end public class Pixila

